/**
 *  @file  InternetAddress.h version 1.0 2007-11-20 $
 *  @brief Define wrapper class InternetAddress for internet address.
 *  Copyright (c) 2007 by @author Zhao Jun <abitfree@gmail.com>
 */

#ifndef __INTERNET_ADDRESS_H__
#define __INTERNET_ADDRESS_H__

#include "SocketHandle.h"

namespace vs {
namespace net {

/**
@brief Wrapper class for internet address.
This class encapsulates the internet address, and provide easy to use interfaces.
*/
class InternetAddress
{
public:
	/// Default constructor, usually used to prepare parameter for accept().
	InternetAddress(unsigned short port_number=0, unsigned int ip_address = INADDR_ANY, int address_family = AF_INET);

	/// Constructor usually used to prepare parameter for connect().
	InternetAddress(unsigned short port_number, const char host_name[], int address_family = AF_INET);

	/// Copy constructor.
	InternetAddress(const InternetAddress &internet_address);

	///@{
	/// Setter functions.
	void Set(const InternetAddress &internet_address);
	void Set(unsigned short port_number, unsigned int ip_address, int address_family);
	void Set(unsigned short port_number, const char host_name[], int address_family = AF_INET);
	///@}
	
	/// Convert to string format "host:port".
	int ToString(char buffer[], size_t size, bool with_port = false) const;

	///@{
	/// Getter functions.
	unsigned short GetPort(void) const;
	int GetHostName(char host_name[], size_t length) const;
	const char* GetHostName(void) const;
	///@}
	
	/// Get the underlying internet address struct.
	void* GetAddress() const;
	
	/// Get the underlying internet address size.
	size_t GetAddressSize() const;

	/// Set the underlying internet address size.
	void SetAddressSize(size_t size);

	///@{
	/// Compare with another internet address.
	bool operator == (const InternetAddress &rhs) const;
	bool operator != (const InternetAddress &rhs) const;
	bool IsIpEqual(const InternetAddress &rhs) const;
	///@}

private:
	// Reset the underlying internet address to default value.
	void Reset(int address_family = AF_INET);

	// Dump the detail information for debugging.
	void Dump(void) const;
	
	// now, only for inet4.
	sockaddr_in internet_address_;

	/// Internet address size.
	SOCKETLEN_T address_length_;
	
	
	// for inet6
	union
	{
		sockaddr_in  in4_;
#if defined (ACE_HAS_IPV6)
		sockaddr_in6 in6_;
#endif /* ACE_HAS_IPV6 */
	} inet_addr_;

};

INLINE
InternetAddress::InternetAddress(unsigned short port_number, unsigned int ip_address, int address_family)
{
	Set(port_number, ip_address, address_family);
}

INLINE
InternetAddress::InternetAddress(unsigned short port_number, const char host_name[], int address_family)
{
	Set(port_number, host_name, address_family);
}

INLINE
InternetAddress::InternetAddress(const InternetAddress &internet_address)
{
	Set(internet_address);
}

INLINE
void InternetAddress::Set(const InternetAddress &internet_address)
{
	memcpy(&internet_address_, &internet_address.internet_address_, sizeof(internet_address_));
	address_length_ = internet_address.address_length_;

	// todo : just now, ignore inet_addr_.
}

INLINE
void InternetAddress::Set(unsigned short port_number, unsigned int ip_address, int address_family)
{
	Reset(address_family);
	internet_address_.sin_port = htons(port_number);
	internet_address_.sin_addr.s_addr = htonl(ip_address);
}

INLINE
void InternetAddress::Set(unsigned short port_number, const char host_name[], int address_family)
{
	Reset(address_family);
	internet_address_.sin_port = htons(port_number);
	if (host_name == NULL)
	{
		internet_address_.sin_addr.s_addr = htonl(INADDR_ANY);
	}
	else
	{
#ifdef WIN32
		internet_address_.sin_addr.s_addr = inet_addr(host_name);
#else
		inet_pton(address_family, host_name, &internet_address_.sin_addr);
#endif
		if (internet_address_.sin_addr.s_addr == INADDR_NONE)
		{
			struct hostent *host = gethostbyname(host_name);
			if (host == NULL)
			{
				printf("unable to resolve server : %s\n", host_name);
			}
			else
			{
				memcpy(&internet_address_.sin_addr, host->h_addr_list[0], host->h_length);
			}
		}
	}
}

INLINE
int InternetAddress::ToString(char buffer[], size_t size, bool with_port) const
{
	const int MAX_HOST_NAME_LENGTH = 128;
	char  host_name[MAX_HOST_NAME_LENGTH + 1];

	bool result = false;
	result = (GetHostName(host_name, MAX_HOST_NAME_LENGTH + 1) == 0);

	if (!result)
		return -1;

	char *format = "%s";
	size_t length = strlen(host_name);
	if (with_port)
	{
		length = length + 5 + 1 + 1;
		format = "%s:%d";
	}
	
    if (size < length)
		return length;
	else
		sprintf (buffer, format, host_name, GetPort());
	return 0;
}

INLINE
unsigned short InternetAddress::GetPort(void) const
{
	return ntohs(internet_address_.sin_port);
}

INLINE
int InternetAddress::GetHostName(char host_name[], size_t length) const
{
	char *host = inet_ntoa(internet_address_.sin_addr);
	strncpy(host_name, host, length);
	return 0;
}

INLINE
const char* InternetAddress::GetHostName(void) const
{
	return inet_ntoa(internet_address_.sin_addr);
}

INLINE
void* InternetAddress::GetAddress() const
{
	return (void*)&internet_address_;
}

INLINE
size_t InternetAddress::GetAddressSize() const
{
	return (address_length_ != 0) ? address_length_ : sizeof(internet_address_);
}

INLINE
void InternetAddress::SetAddressSize(size_t size)
{
	address_length_ = size;
}

INLINE
bool InternetAddress::operator == (const InternetAddress &rhs) const
{
	return false;
}

INLINE
bool InternetAddress::operator != (const InternetAddress &rhs) const
{
	return false;
}

INLINE
bool InternetAddress::IsIpEqual(const InternetAddress &rhs) const
{
	return false;
}

INLINE
void InternetAddress::Dump(void) const
{
}

INLINE
void InternetAddress::Reset(int address_family)
{
	memset((void*)&internet_address_, 0, sizeof(internet_address_));
	internet_address_.sin_family = address_family;
	address_length_ = 0;
}

}	// namespace net
}	// namespace vs


#endif	// __INTERNET_ADDRESS_H__
